package com.github.tonydeng.fmj.runner;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.StrUtil;
import com.github.tonydeng.fmj.handler.DefaultCallbackHandler;
import com.github.tonydeng.fmj.handler.ProcessCallbackHandler;
import com.github.tonydeng.fmj.pool.RedisOpsUtil;
import com.github.tonydeng.fmj.utils.ChiaUtils;
import com.google.common.base.Stopwatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Created by tonydeng on 15/4/16.
 */
@Component
public class ChiaCommandRunner {
    private static final Logger log = LoggerFactory.getLogger(ChiaCommandRunner.class);

    @Autowired
    public RedisOpsUtil redisOpsUtil;

    /**
     * p盘任务
     *
     * @return
     */
    public void chiaPlots(String workId, String size, String num, String bytes,
                                 String theadNum, String buckets,
                                 String farmerKey, String poolKey,
                                 String tmpDir, String tmp2Dir, String finalDir, String dCount) {

        List<String> commands = CollUtil.newArrayList(BaseChiaCommandOption.getChiaPlotsBinary());
        commands.addAll(BaseChiaCommandOption.toPlotsCmdArrays(redisOpsUtil, workId, size, num, bytes, theadNum, buckets, farmerKey, poolKey, tmpDir, tmp2Dir, finalDir));
        runProcess(workId, dCount, commands);
    }

    /**
     * 执行命令
     *
     * @param commands
     * @return
     */
    public String runProcess(String workId, String dCount, List<String> commands) {
        try {
            return runProcess(workId, dCount, commands, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 执行命令
     *
     * @param commands
     * @param handler
     * @return
     * @throws Exception
     */
    public String runProcess(String workId, String dCount, List<String> commands, ProcessCallbackHandler handler) {
        ProcessBuilder pb = null;
        Process process = null;
        FileWriter writer = new FileWriter("/home/chia/diskLog/" + workId + ".log");

        if (log.isInfoEnabled()){
            writer.append(StrUtil.format("chia start workId {} to run chia process... cmd : '{}'", workId, ChiaUtils.chiaCmdLine(commands)));
            log.info("chia start workId {} to run chia process... cmd : '{}'", workId, ChiaUtils.chiaCmdLine(commands));
        }

        Stopwatch stopwatch = Stopwatch.createStarted();
        pb = new ProcessBuilder(commands);

        pb.redirectErrorStream(true);

        if (null == handler) {
            handler = new DefaultCallbackHandler();
        }

        String result = null;
        try {
            process = pb.start();

            //记录整体任务开始时间
            String firstStartTime = redisOpsUtil.getValue("firstStartTime");
            if(StrUtil.isEmpty(firstStartTime)){
                redisOpsUtil.setValue("firstStartTime", DateUtil.now());
            }

            //记录p盘任务开始
            redisOpsUtil.setValue("plot_start_" + dCount + "_" + workId, "1");

            //记录硬盘的工作进程数
            redisOpsUtil.setValue("plot_working_" + dCount + "_" + workId, "1");

            result = handler.handler(workId, dCount, process.getInputStream(), redisOpsUtil);
            //记录p盘任务结束
            redisOpsUtil.setValue("plot_end_" + dCount + "_" + workId, "1");
            //删除正在工作的记录
            redisOpsUtil.getValue("working_" + dCount + "_" + workId);

            //删除硬盘的工作进程数
            redisOpsUtil.delete("plot_working_" + dCount + "_" + workId);
        } catch (Exception e) {
            writer.append(StrUtil.format("errorStream:{}", result, e));

            log.error("errorStream:{}", result, e);
        } finally {
            if (null != process) {
                try {
                    process.getInputStream().close();
                    process.getOutputStream().close();
                    process.getErrorStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        try {
            int flag = process.waitFor();
            if (flag != 0) {
                //记录p盘任务异常结束
                redisOpsUtil.setValue("plot_error_" + dCount + "_" + workId, "1");
                //删除正在工作的记录
                redisOpsUtil.getValue("working_" + dCount + "_" + workId);
                //删除p盘任务结束
                redisOpsUtil.delete("plot_end_" + dCount + "_" + workId);

                writer.append(StrUtil.format("chia workId {} process exit with error value:{}", workId, flag));

                log.error("chia workId {} process exit with error value:{}", workId, flag);
                throw new IllegalThreadStateException("process exit with error value : " + flag);
            }
        } catch (InterruptedException e) {
            log.error("wait for process finish error:{}", e);
        } finally {
            if (null != process) {
                process.destroy();
                pb = null;
            }

            stopwatch.stop();
        }
        if (log.isInfoEnabled()) {
            log.info("chia finish workId {} run {} seconds, {} milliseconds",
                    workId,
                    stopwatch.elapsed(TimeUnit.SECONDS),
                    stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }
        return result;
    }
}